package chan.module.project.hw.mrs.ck.v331.test;

import chan.module.project.comm.ClickHouseUtil;
import chan.module.project.comm.FieldMeta;
import chan.module.project.comm.KeyTabUtil;
import chan.module.project.comm.PropertiesUtil;
import com.clickhouse.client.config.ClickHouseClientOption;
import com.clickhouse.client.config.ClickHouseDefaults;
import com.clickhouse.jdbc.ClickHouseDataSource;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Scanner;

/**
 *
 */
public class MRSClickhouseV331TestMain {
    private static final Logger LOG = LogManager.getLogger(MRSClickhouseV331TestMain.class);
    protected PropertiesUtil propertiesUtil;
    private ClickHouseDataSource balancedClickhouseDataSource;

    public static void main(String[] args) {
        LOG.info("开始执行程序...");
        String command;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            LOG.info("1-解析keyTab文件成Base64内容； 2-执行SQL； 其他-退出");
            command = scanner.next();
            if ("1".equals(command)) {
                analysisKeyTab(scanner);
            } else if ("2".equals(command)) {
                MRSClickhouseV331TestMain testClickhouse = new MRSClickhouseV331TestMain();
                try {
                    testClickhouse.propertiesUtil = PropertiesUtil.readConfig(args);
                    Connection connection = testClickhouse.initConnection();
                    testClickhouse.executeSQL(connection);
                } catch (Exception e) {
                    LOG.info("执行SQL程序失败！", e);
                }
            } else {
                break;
            }
        }
        LOG.info("程序退出！");
    }

    /**
     * 生成认证模式下的Clickhouse独立连接URL
     * @param scanner
     */
    public static void analysisKeyTab(Scanner scanner) {
        LOG.info("输入keyTab文件路径：");
        String command = scanner.next();
        File file = new File(command);
        if (!file.exists()) {
            LOG.info("keyTab文件[{}]不存在！", command);
            return;
        }

        String keyTabPath = file.getAbsolutePath();
        String connInfoPath =  "connInfo_"+System.currentTimeMillis() + ".txt";
        connInfoPath = file.getParent() + "/" + connInfoPath;
        String keyTabBase64Txt = KeyTabUtil.readFile(keyTabPath);
        LOG.info("keyTabBase64内容：{}", keyTabBase64Txt);

        File connInfoFile = new File(connInfoPath);
        StringBuilder connInfoSb = new StringBuilder();
        connInfoSb.append("keyTabBase64内容：\n").append(keyTabBase64Txt).append("\n");

        try {
            FileUtils.writeStringToFile(connInfoFile, connInfoSb.toString(), "UTF-8");
            LOG.info("连接信息已经写入到文件：{}", connInfoPath);
        } catch (Exception e) {
            LOG.info("连接信息已经写入到文件[{}]失败！", connInfoPath, e);
        }
    }

    public Connection initConnection() throws Exception {
        Properties properties = propertiesUtil.getProp();
        Class.forName(properties.getProperty("mrs.clickhouse.driver"));

        Properties clickHouseProperties = new Properties();
        boolean isSec = Boolean.parseBoolean(properties.getProperty("mrs.clickhouse.security.enabled"));

        String url = properties.getProperty("mrs.clickhouse.datasource.url");

        String userName = properties.getProperty("mrs.clickhouse.user");
        boolean isMachineUser = Boolean.parseBoolean(properties.getProperty("mrs.clickhouse.is.machine_user"));
        if (isSec) {
            if (isMachineUser) {
                clickHouseProperties.setProperty("isMachineUser", "true");
                clickHouseProperties.setProperty("keytabPath", properties.getProperty("mrs.clickhouse.keytab_path"));
            }
            String userPass = properties.getProperty("mrs.clickhouse.password");
            clickHouseProperties.setProperty(ClickHouseDefaults.PASSWORD.getKey(), userPass);
            clickHouseProperties.setProperty(ClickHouseClientOption.SSL.getKey(), Boolean.toString(true));
            clickHouseProperties.setProperty(ClickHouseClientOption.SSL_MODE.getKey(), "none");
        }
        clickHouseProperties.setProperty(ClickHouseDefaults.USER.getKey(), userName);

        Connection connection;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            clickHouseProperties.setProperty(ClickHouseClientOption.FAILOVER.getKey(), "21");
            clickHouseProperties.setProperty(ClickHouseClientOption.LOAD_BALANCING_POLICY.getKey(), "roundRobin");
            LOG.info("配置数据库连接信息完成，准备连接....");
            balancedClickhouseDataSource = new ClickHouseDataSource(url, clickHouseProperties);
            connection = balancedClickhouseDataSource.getConnection();
            LOG.info("连接完成，准备进行测试连接...");
            statement = connection.createStatement();
            String testSql = properties.getProperty("mrs.clickhouse.sql.test");
            LOG.info("测试连接SQL语句：{}", testSql);
            resultSet = statement.executeQuery(testSql);
            while (resultSet.next()) {
                LOG.info("查询结果：{}", resultSet.getInt(1));
            }
            LOG.info("连接成功！");
            // 是否需要保活
            String keepAliveTime = properties.getProperty("mrs.clickhouse.keep-alive.time");
            if (keepAliveTime != null) {
                long time = Long.parseLong(keepAliveTime);
                time = time < 5 ? 5 : time;
                LOG.info("保活连接时间{}秒", time);
                long finalTime = time * 1000L;
                Thread keepAliveTimeThread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        LOG.info("开启保活连接线程");
                        while (true) {
                            try {
                                if (connection.isClosed()) {
                                    LOG.info("退出保活线程！");
                                    break;
                                }
                                Statement keepAliveStatement = connection.createStatement();
                                ResultSet keepAliveResultSet = keepAliveStatement.executeQuery(testSql);
                                ClickHouseUtil.close(keepAliveResultSet, keepAliveStatement, null);
                                Thread.sleep(finalTime);
                            } catch (Exception e) {
                                LOG.info("退出保活线程！");
                                LOG.info("保活失败！", e);
                                break;
                            }
                        }
                    }
                });
                keepAliveTimeThread.start();
            }
        } catch (Exception e) {
            LOG.info("连接数据库失败！");
            throw e;
        } finally {
            ClickHouseUtil.close(resultSet, statement, null);
        }
        return connection;
    }

    /**
     * 执行SQL
     *
     * @param connection
     */
    public void executeSQL(Connection connection) {
        if (connection == null) {
            LOG.info("连接为空，不执行查询！");
            return;
        }
        Properties properties = propertiesUtil.getProp();
        String queryFile = properties.getProperty("mrs.clickhouse.sql.query.file");
        String scriptFile = properties.getProperty("mrs.clickhouse.sql.script.file");
        // 命令
        String command;
        Scanner scanner = new Scanner(System.in);
        Statement statement = null;
        ResultSet resultSet = null;

        while (true) {
            LOG.info("输入执行命令：");
            LOG.info("1-执行查询SQL；2-执行SQL脚本；其他-退出");
            command = scanner.next();
            if (!"1".equals(command) && !"2".equals(command)) {
                break;
            }

            try {
                boolean isQuery = "1".equals(command);
                String chooseSqlFile = isQuery ? queryFile : scriptFile;
                File file = new File(chooseSqlFile);
                String sql = FileUtils.readFileToString(file, "UTF-8");
                LOG.info("准备执行SQL: {}", sql);
                statement = connection.createStatement();
                // 执行SQL脚本
                if (isQuery) {
                    // 执行SQL查询
                    resultSet = statement.executeQuery(sql);
                    ResultSetMetaData metaData = resultSet.getMetaData();
                    int columnCount = metaData.getColumnCount();
                    List<FieldMeta> fieldMetaList = new ArrayList<>(columnCount);
                    for (int i = 1; i <= columnCount; i++) {
                        FieldMeta fieldMeta = new FieldMeta(metaData, i);
                        fieldMetaList.add(fieldMeta);
                        LOG.info("结果字段元信息：{}", fieldMeta.toString());
                    }
                    int row = 0;
                    while (resultSet.next()) {
                        row++;
                        LOG.info(">>>>>第{}行：", row);
                        for (FieldMeta fieldMeta : fieldMetaList) {
                            String originalColumnName = fieldMeta.getOriginalColumnName();
                            LOG.info("{} : {}", originalColumnName, resultSet.getString(originalColumnName));
                        }
                        LOG.info("--------------------");
                    }
                    ClickHouseUtil.close(resultSet, statement, null);
                    statement = null;
                    resultSet = null;
                } else {
                    boolean execute = statement.execute(sql);
                    LOG.info("执行SQL完成！结果：{}", execute);
                    ClickHouseUtil.close(null, statement, null);
                    statement = null;
                }
            } catch (Exception e) {
                ClickHouseUtil.close(resultSet, statement, null);
                LOG.info("执行失败！", e);
            }
        }
        ClickHouseUtil.close(resultSet, statement, connection);
        LOG.info("执行SQL结束。");
    }
}
